package fitness;

import java.io.Serializable;

import optimizers.commons.GAConstants;
import optimizers.ganeat.fcd.ConstantsFCD;
import util.MyUtil;
import model.Input;
import model.OligoSystemGeneral;

public class FCDFitness extends Fitness implements Serializable {

	public FCDResult evaluateFitness(OligoSystemGeneral oligoModel) {
		FCDResult result = new FCDResult();

		result.mse = new double[ConstantsFCD.nTests];
		result.amplitude_e = new double[ConstantsFCD.nTests];
		result.reaction_time_e = new int[ConstantsFCD.nTests];
		result.sensitivity = new double[ConstantsFCD.nTests];
		result.precision = new double[ConstantsFCD.nTests];
		double[] inputsAmmount = MyUtil.getInputsForFCD(ConstantsFCD.minTest,
				ConstantsFCD.maxTest, ConstantsFCD.nTests);
		Input[] inputs = new Input[inputsAmmount.length];
		for (int i = 0; i < inputsAmmount.length; i++) {
			inputs[i] = new Input(1, inputsAmmount[i], Integer.MAX_VALUE);
		}

		boolean[] sequenceProtected = new boolean[oligoModel.nSimpleSequences];
		sequenceProtected[0] = true;

		for (int i = 1; i < oligoModel.nSimpleSequences; i++) {
			sequenceProtected[i] = false;
		}
		oligoModel.sequenceProtected = sequenceProtected;
		double[] reporterConcentration = new double[oligoModel.nSimpleSequences];
		for (int i = 0; i < oligoModel.nSimpleSequences; i++) {
			reporterConcentration[i] = 0;
		}
		reporterConcentration[1] = ConstantsFCD.reporterConcentration;
		oligoModel.reporterConcentration = reporterConcentration;
		oligoModel.seqConcentration[0][0] = ConstantsFCD.minTest;
		double[][] data = oligoModel.getTimeSeriesWithInputComplexModel(inputs);
		int output = data.length - 1;
		for (int i = 1; i < inputs.length - 2; i++) {
			if (Math.abs(inputs[i].timeStep) >= GAConstants.maxTime
					|| Math.abs(inputs[i + 1].timeStep) >= GAConstants.maxTime) {
				result.sensitivity[i] = 0;
				result.precision[i] = 0;
			} else {
				int peakTime = MyUtil.getFirstPeak(data[output],
						inputs[i].timeStep);
				if (peakTime == -1 != peakTime >= inputs[i+1].timeStep) {
					result.sensitivity[i] = 0;
					result.precision[i] = 0;
				} else {
					result.sensitivity[i] = data[output][peakTime]
							- data[output][inputs[i].timeStep];
					if (result.sensitivity[i] > 1) {
						result.precision[i] = 1 / Math
								.abs(data[output][inputs[i + 1].timeStep]
										- data[output][inputs[i].timeStep]);
					} else {
						result.precision[i] = 0;
					}
				}
			}
			if (Math.abs(data[output][inputs[i].timeStep - 1]
					- data[output][inputs[i].timeStep + 1]) < 1
					|| Math.abs(data[output][inputs[i + 1].timeStep - 1]
							- data[output][inputs[i].timeStep + 1]) > 1) {
				result.amplitude_e[i] = Double.MAX_VALUE;
				result.mse[i] = Double.MAX_VALUE;
				result.reaction_time_e[i] = Integer.MAX_VALUE;
			} else {
				double mse = 0;
				int count = 0;
				int reaction_time1 = inputs[i + 1].timeStep
						- inputs[i].timeStep;
				int reaction_time2 = inputs[i + 2].timeStep
						- inputs[i + 1].timeStep;
				double peak1 = Integer.MIN_VALUE;
				double peak2 = Integer.MIN_VALUE;
				for (int j = inputs[i].timeStep; j < inputs[i + 1].timeStep; j++) {
					mse += (data[output][j] - data[output][j + reaction_time1])
							* (data[output][j] - data[output][j
									+ reaction_time1]);
					if (peak1 < data[output][j])
						peak1 = data[output][j];
					if (peak2 < data[output][j + reaction_time1])
						peak2 = data[output][j + reaction_time1];
					count++;
				}
				mse /= count;
				result.amplitude_e[i] = Math.abs(peak2 - peak1);
				result.mse[i] = mse;
				result.reaction_time_e[i] = Math.abs(reaction_time2
						- reaction_time1);
			}
		}
		return result;
	}

	@Override
	public FitnessResult minFitness(OligoSystemGeneral indiv) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public FitnessResult evaluateFitnessSimple(OligoSystemGeneral indiv) {
		// TODO Auto-generated method stub
		return null;
	}

}
